/* autor Konjkov.VV@gmail.com */

#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"
#include "packet.h"
#include "packetinfo.h"

#define GENERAL_SECTION_START "# === General packet description\n\n"
#define GENERAL_SECTION_STOP  "# === End general packet description\n\n"

#define TABLES_SECTION_START "# === Description tables\n\n"
#define TABLES_SECTION_STOP  "# === End tables description\n"

#define TABLE_SECTION_START  "# --- Description table %s.%s\n\n"
#define TABLE_SECTION_STOP   "# --- End description\n\n"

// имя файла, в котором хранится информация
#define FILENAME_PACKINFO  "packet.info"
// версия формата пакета
#define PACKINFO_PACKET_VER_PACKET "packet_version"
// версия системы, создавшей пакет
#define PACKINFO_PACKET_VER_SYSTEM "system_version"
// уровень безопасности пакета
#define PACKINFO_PACKET_SEC_LEVEL  "packet_security_level"
// номер пакета
#define PACKINFO_PACKET_NUMBER     "packet_number"
// номер предыдущего пакета
#define PACKINFO_PACKET_PREV       "packet_prev"
// от какого узла пакет
#define PACKINFO_PACKET_FROM       "packet_from"
// какому узлу предназначен пакет
#define PACKINFO_PACKET_TO         "packet_to"

// список ключевых полей
#define PACKINFO_TABLE_PKEYS       "pkey_fields"
// список остальных полей
#define PACKINFO_TABLE_OTHER       "other_fields"
// описание всех полей, как
#define PACKINFO_TABLE_CREATE      "create_clause"

int packetbegin(FILE *packet_info, char* node, int prevpacket, int curpacket) {
// Создание файла packet.info
int  seclevel = 0;
char packet_version[10] = "2.0";

//    packet_info = fopen(FILENAME_PACKINFO, "w");
    fprintf(packet_info, GENERAL_SECTION_START);
    fprintf(packet_info, PACKINFO_PACKET_SEC_LEVEL"=%i\n", seclevel);
    fprintf(packet_info, PACKINFO_PACKET_VER_PACKET"=%s\n", packet_version);
    fprintf(packet_info, PACKINFO_PACKET_VER_SYSTEM"=%s\n", SYSTEM_VER);
    fprintf(packet_info, PACKINFO_PACKET_FROM"=%s\n", THIS);
    fprintf(packet_info, PACKINFO_PACKET_TO"=%s\n", node);
    fprintf(packet_info, PACKINFO_PACKET_NUMBER"=%i\n", curpacket+1);
    fprintf(packet_info, PACKINFO_PACKET_PREV"=%i\n\n", prevpacket);
    fprintf(packet_info, GENERAL_SECTION_STOP);

    fprintf(packet_info, TABLES_SECTION_START);

    return 0;
}

int tablesection_pkeyfields(FILE *packet_info, char* owner, char* table) {
// Получить список полей первичного ключа
struct field    primarykey;
struct field*   nextfield;
char            pkeyfields[MAX_NAME_LEN*64];
int             i=0;

    tableprimarykeys(owner, table, &primarykey);
    nextfield = &primarykey;
    while (nextfield != NULL) {
        i += sprintf(pkeyfields + i, "%s,", nextfield->name);
        nextfield = nextfield->next;
    }
    pkeyfields[i-1] = '\0';
    fprintf(packet_info, TABLE_SECTION_START, owner, table);
    fprintf(packet_info, PACKINFO_TABLE_PKEYS"='%s'\n", pkeyfields);
    return 0;
}

int tablesection_otherfields(FILE *packet_info, char* owner, char* table) {
// Получить список всех полей кроме полей первичного ключа
struct field    other;
struct field*   nextfield;
char            otherfields[MAX_NAME_LEN*64];
int             j=0;

    tableotherfields(owner, table, &other);
    nextfield = &other;
    while(nextfield != NULL) {
        j += sprintf(otherfields + j, "%s,", nextfield->name);
        nextfield = nextfield->next;
    }
    otherfields[j-1] = '\0';
    fprintf(packet_info, PACKINFO_TABLE_OTHER"='%s'\n", otherfields);
    return 0;
}

int tablesection_createclause(FILE *packet_info, SQLHSTMT* stmt) {
// Получить спиcок полей как в запросе CREATE TABLE
SQLRETURN     retcode;

int k=0;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnSize;
SQLSMALLINT   DataType, DecimalDigits;

SQLSMALLINT   numcols;
int           col;

char          createclause[MAX_NAME_LEN*64];

    EXEC_SQL( SQLNumResultCols( stmt, &numcols) );
    for (col = 0; col < numcols; col++) {
        EXEC_SQL( SQLDescribeCol( stmt, col+1, ColumnName, MAX_NAME_LEN, NULL, &DataType, &ColumnSize, &DecimalDigits, NULL));
        if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
            switch (DataType) {
            case SQL_GUID:
                k += sprintf(createclause + k, "%s varchar(%d),", ColumnName, ColumnSize);
                break;
            case SQL_BIT:
                k += sprintf(createclause + k, "%s bit,", ColumnName);
                break;
            case SQL_TINYINT:
                k += sprintf(createclause + k, "%s tinyint,", ColumnName);
                break;
            case SQL_BIGINT:
                k += sprintf(createclause + k, "%s bigint,", ColumnName);
                break;
            case SQL_LONGVARBINARY:
                k += sprintf(createclause + k, "%s longvarbinary,", ColumnName);
                break;
            case SQL_VARBINARY:
                k += sprintf(createclause + k, "%s varbinary,", ColumnName);
                break;
            case SQL_BINARY:
                k += sprintf(createclause + k, "%s binary,", ColumnName);
                break;
            case SQL_LONGVARCHAR:
                k += sprintf(createclause + k, "%s longvarchar(%d),", ColumnName, ColumnSize);
                break;
            case SQL_CHAR:
                k += sprintf(createclause + k, "%s char(%d),", ColumnName, ColumnSize);
                break;
            case SQL_NUMERIC:
                k += sprintf(createclause + k, "%s numeric(%d,%d),", ColumnName, ColumnSize, DecimalDigits);
                break;
            case SQL_DECIMAL:
                k += sprintf(createclause + k, "%s decimal(%d,%d),", ColumnName, ColumnSize, DecimalDigits);
                break;
            case SQL_INTEGER:
                k += sprintf(createclause + k, "%s integer,", ColumnName, ColumnSize);
                break;
            case SQL_SMALLINT:
                k += sprintf(createclause + k, "%s smallint,", ColumnName);
                break;
            case SQL_FLOAT:
                k += sprintf(createclause + k, "%s float,", ColumnName);
                break;
            case SQL_REAL:
                k += sprintf(createclause + k, "%s real,", ColumnName);
                break;
            case SQL_DOUBLE:
                k += sprintf(createclause + k, "%s double,", ColumnName);
                break;
            case SQL_TIME:
                k += sprintf(createclause + k, "%s time,", ColumnName);
                break;
            case SQL_TIMESTAMP:
                k += sprintf(createclause + k, "%s timestamp,", ColumnName);
                break;
            case SQL_DATETIME:
                k += sprintf(createclause + k, "%s datetime,", ColumnName);
                break;
            case SQL_VARCHAR:
                k += sprintf(createclause + k, "%s varchar(%d),", ColumnName, ColumnSize);
                break;
            default:
                k += sprintf(createclause + k, "%s %d", ColumnName, DataType);
                break;
            }
        }
    }
    createclause[k-1] = '\0';
    fprintf(packet_info, PACKINFO_TABLE_CREATE"='%s'\n\n", createclause);
    fprintf(packet_info, TABLE_SECTION_STOP);

    return 0;
}

int packetend(FILE *packet_info) {
    fprintf(packet_info, TABLES_SECTION_STOP);
    fclose(packet_info);
}

int tableprimarykeys(char* owner, char* table, struct field* fields) {
// Получить список полей первичного ключа
SQLHSTMT      stmt;
SQLRETURN     retcode;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnNameSize;

struct field* newfield = NULL;

    SQLAllocStmt( dbc, &stmt );
    EXEC_SQL( SQLPrimaryKeys( stmt, NULL, 0, owner, SQL_NTS, table, SQL_NTS) );
    SQLBindCol( stmt, 4, SQL_C_CHAR, ColumnName, MAX_NAME_LEN, &ColumnNameSize);
    while(1) {
        retcode = SQLFetch(stmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
                if (newfield == NULL) {
                    newfield = fields;
                } else {
                    newfield->next = malloc(sizeof(struct field));
                    newfield = newfield->next;
                }
                newfield->name = malloc(ColumnNameSize*sizeof(char)+1);
                strcpy(newfield->name, ColumnName);
                newfield->next=NULL;
            }
        } else if (retcode == SQL_NO_DATA){
            break;
        } else {
            return syncctl_error( stmt , __FILE__, __LINE__);
        }
    }
    SQLFreeStmt( stmt, SQL_CLOSE );
    return 0;
}

int tableallfields(char* owner, char* table, struct field* fields) {
// Получить список всех полей таблицы
SQLHSTMT      stmt;
SQLRETURN     retcode;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnNameSize;

struct field* newfield = NULL;

    SQLAllocStmt( dbc, &stmt );
    EXEC_SQL( SQLColumns( stmt, NULL, 0, owner, SQL_NTS, table, SQL_NTS, NULL, 0) );
    SQLBindCol( stmt, 4, SQL_C_CHAR, ColumnName, MAX_NAME_LEN, &ColumnNameSize);
    while(1) {
        retcode = SQLFetch(stmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
                if (newfield == NULL) {
                    newfield = fields;
                } else {
                    newfield->next = malloc(sizeof(struct field));
                    newfield = newfield->next;
                }
                newfield->name = malloc(ColumnNameSize*sizeof(char)+1);
                strcpy(newfield->name, ColumnName);
                newfield->next=NULL;
            }
        } else if (retcode == SQL_NO_DATA){
            break;
        } else {
            return syncctl_error( stmt , __FILE__, __LINE__);
        }
    }
    SQLFreeStmt( stmt, SQL_CLOSE );
    return 0;
}

int tableotherfields(char* owner, char* table, struct field* fields) {
// Получить список всех полей таблицы кроме ключевых
struct field primaryfields, allfields;
struct field *nextprimaryfield, *nextallfield;
struct field* newfield = NULL;
int          flag = 0;

    tableallfields(owner, table, &allfields);
    tableprimarykeys(owner, table, &primaryfields);
    nextallfield = &allfields;
    while (nextallfield != NULL) {
        nextprimaryfield = &primaryfields;
        flag = 0;
        while (nextprimaryfield != NULL) {
            if (!strcmp(nextprimaryfield->name, nextallfield->name)) {
                flag = 1;
                break;
            }
            nextprimaryfield = nextprimaryfield->next;
        }
        if (flag == 0) {
            if (newfield == NULL) {
                newfield = fields;
            } else {
                newfield->next = malloc(sizeof(struct field));
                newfield = newfield->next;
            }
            newfield->name = malloc(256*sizeof(char));
            strcpy(newfield->name, nextallfield->name);
            newfield->next=NULL;
        }
        nextallfield = nextallfield->next;
    }

    return 0;
}
